
/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "ui3D场景视口.h"
#include <几何图形/图形相交.h>
#include <物体/S_摄像机.h>
#include <场景/intern/S_Scene.h>
#include <core/几何计算/多边形.h>
#include <core/几何计算/相交.h>

#include <节点编辑.h>

#include "框架/工作区/节点视口/节点类型构建/物体节点构建.h"
#include "框架/工作区/节点视口/ui节点视口.h"

#include "物体编辑/3D控制手柄.h"
#include "物体编辑/物体编辑.h"
#include "物体编辑/视口辅助物体.h"
#include "物体管理/渲染物体约束更新.h"


#include "框架/视口元素.h"
#include "B_场景管理/相机/相机控制.h"
#include "B_场景管理/节点管理.h"

#include "渲染/场景绘制/视口物体绘制.h"
#include "物体管理/渲染物体管理.h"
#include "物体管理/渲染物体创建销毁.h"


//static std::vector<S_编辑物体更新节点>	g当前拾取节点更新方式;
static vec2 g选框起点坐标;
static vec2 g选框终点坐标;
static bool g打开框选 = false;
static S_RGBA8UI g选框边颜色 = S_主题颜色::uic_物体选框边颜色;

bool  C_3D场景视口::g加选 = false;

bool  C_3D场景视口::g鼠标左键是否按下 = false;
bool  C_3D场景视口::g视口摄像机旋转 = false;
uint8 C_3D场景视口::g视口摄像机平移 = 0;

uint8 C_3D场景视口::g_移动视口_鼠标左键 = 1;
uint8 C_3D场景视口::g_移动视口_键盘按键 = 2;
uint8 C_3D场景视口::g_移动视口_鼠标中键 = 4;


std::set<C_3D场景视口*> C_3D场景视口::gui_3D视口;




static void on_属性修改更新节点(S_Props& prop) {
	std::vector<C_节点基类*> nodes;
	if (f_prop_Bool(S_节点数据状态::g_是否打开了编辑模式)) {
		nodes = f_NodeCtx_从可视物体取节点(S_节点数据::g_编辑物体);
	}
	else {
		nodes = f_NodeCtx_从可视物体取节点(S_框架::g_拾取物体);
	}

	for (auto 节点 : nodes) {
		节点->f_set是否要更新(true);
	}
}

static void f_开启视口调试_状态栏更新(S_Props& prop) {
	C_工作区页面* 页面 = static_cast<C_工作区页面*>(prop.m_UI->m_部件->m_父部件->m_父部件->m_父部件);
	C_3D场景视口* 视口 = static_cast<C_3D场景视口*>(页面->mui_视口页面);

	f_view3d_重构状态条((C_状态条*)prop.m_UI->m_部件->m_父部件->m_父部件, 视口->ui_三维视口);
}





static E_事件是否传递 on_Widget鼠标进入(C_Widget* self, const S_鼠标& 鼠标) {
	S_节点数据::g_当前操作视口相机 = f_ui_get3D视口摄像机(self);
	return E_事件是否传递::e_事件传递_继续;
}

static void on_3D视口物体变换模式(S_Props& prop) {
	auto val = f_prop_enum(prop);

	for (auto& e : C_3D场景视口::gui_3D视口) {
		f_辅助物体_切换变换手柄类型(e->m_变换手柄, E_变换(val));
	}
	
	for (auto& e : C_3D场景视口::gui_3D视口) {
		f_widget_更新绘制(e);
	}

	f_widget_更新绘制(prop.m_UI->m_部件->m_父部件);
}

void on_骨骼编辑操作模式切换(S_Props& prop) {
	switch (f_prop_enum(prop)) {
		case E_骨骼编辑模式::e_骨架模式: {
			f_prop_Bool(S_节点数据状态::g_是否打开了编辑模式) = false;

			for (auto& e : S_框架::g_拾取物体) {
				if (e->m_Type == E_物体类型::t_骨架) {
					f_ob_from骨架(e)->m_解算约束 = true;
				}
			}

			f_NodeCtx_clear选中编辑物体();
			break;
		}
		
		case E_骨骼编辑模式::e_编辑模式:
			for (auto& e : S_框架::g_拾取物体) {
				if (e->m_Type == E_物体类型::t_骨架) {
					f_ob_from骨架(e)->m_解算约束 = false;
				}
			}

		case E_骨骼编辑模式::e_姿态模式: {
			f_prop_Bool(S_节点数据状态::g_是否打开了编辑模式) = true;

			//去当前编辑节点
			for (auto& e : S_框架::g_拾取物体) {
				if (e->m_Type == E_物体类型::t_骨架) {
					//f_ob_from骨架(e)->m_解算约束 = false;
					f_ob_骨节复位(e);
				}
			}
			break;
		}
		default:
			break;
	}


	C_工作区页面* page = static_cast<C_工作区页面*>(prop.m_UI->m_部件->m_父部件->m_父部件->m_父部件);
	C_3D场景视口* 视口 = static_cast<C_3D场景视口*>(page->mui_视口页面);
	f_view3d_重构状态条((C_状态条*)prop.m_UI->m_部件->m_父部件->m_父部件, 视口->ui_三维视口);

}


static void on_3D视口物体全局显示模式(S_Props& prop) {
	uint32 fb = f_prop_UI32位域枚举(prop);

	for (auto& e : C_3D场景视口::gui_3D视口) {
		f_widget_更新绘制(e->ui_状态条);
		f_widget_更新绘制(e->m_父部件);
	}

}

static void on_3D视口物体全局渲染模式(S_Props& prop) {
	uint32 fb = f_prop_enum(prop);
	f_NodeCtx_set物体全局渲染模式(S_节点数据::g_物体全局显示模式, E_物体渲染模式(fb));

	if (prop.m_UI->m_部件) {
		C_工作区页面* page = static_cast<C_工作区页面*>(prop.m_UI->m_部件->m_父部件->m_父部件->m_父部件);
		f_ui_切换3D视口渲染模式(static_cast<C_3D场景视口*>(page->mui_视口页面)->ui_三维视口, E_物体渲染模式(fb));
	}


	switch (fb) {
		case E_物体渲染模式::e_渲染模式_白模: {
			f_scene_渲染器关闭预览(f_NodeCtx_get默认场景());
			break;
		}
		case E_物体渲染模式::e_渲染模式_纹理: {
			f_scene_渲染器关闭预览(f_NodeCtx_get默认场景());
			break;
		}
		case E_物体渲染模式::e_渲染模式_光栅: {
			f_scene_渲染器关闭预览(f_NodeCtx_get默认场景());
			break;
		}
		case E_物体渲染模式::e_渲染模式_光追: {
			f_scene_渲染器启用预览(f_NodeCtx_get默认场景());
			break;
		}
	}

}

static void on_3D视口_模型元素(S_Props& prop) {
	uint32 模型元素 = f_prop_enum(prop);

	C_工作区页面* 页面 = static_cast<C_工作区页面*>(prop.m_UI->m_部件->m_父部件->m_父部件->m_父部件);
	f_ui_get3D视口渲染帧属性(((C_3D场景视口*)页面->mui_视口页面)->ui_三维视口).m_OutLayer_PreviewMode = DEF_RF_PM_PreviewFlag | (1 << (模型元素 + 16));

	f_render_刷新渲染(f_NodeCtx_get默认场景());
}


static void on_3D视口_面渲染模式(S_Props& prop) {
	uint32 渲染预览模式 = f_prop_enum(prop);

	switch (渲染预览模式) {
		case 0: f_OB_单色网格面材质(*S_节点框架::g_3D视口环境); break;
		case 1: f_OB_ID网格面材质(*S_节点框架::g_3D视口环境); break;
		case 2: f_OB_光照网格面材质(*S_节点框架::g_3D视口环境); break;
	}

	C_工作区页面* 页面 = static_cast<C_工作区页面*>(prop.m_UI->m_部件->m_父部件->m_父部件);
	((C_3D场景视口*)页面->mui_视口页面)->m_更新绘制属性 = true;
	
	f_widget_更新绘制(prop.m_UI->m_部件->m_父部件);
}

static void on_场景打开选择的多物体显示方式(S_Props& prop) {
	uint32 fb = f_prop_UI32位域枚举(prop);

	for (auto& ob : S_框架::g_拾取物体) {
		switch (ob->m_Type) {
			case E_物体类型::t_网格物体:
			case E_物体类型::t_多边形:
				ob->m_视口显示模式 |= 1 << fb;
				break;

			default: break;
		}
	}

	f_render_刷新渲染(f_NodeCtx_get默认场景());
}

static void on_场景关闭选择的多物体显示方式(S_Props& prop) {
	uint32 fb = f_prop_UI32位域枚举(prop);

	for (auto& ob : S_框架::g_拾取物体) {
		switch (ob->m_Type) {
			case E_物体类型::t_网格物体:
			case E_物体类型::t_多边形:
				ob->m_视口显示模式 &= (0xffffffff ^ 1 << fb);
				break;

			default: break;
		}
	}
	f_render_刷新渲染(f_NodeCtx_get默认场景());
}

static void on_切换视口透视模式(S_Props& prop) {
	bool m = f_prop_Bool(prop);
	C_3D场景视口* view = static_cast<C_3D场景视口*>(prop.m_UI->m_部件->m_父部件->m_父部件);

	auto* camera = f_ui_get3D视口摄像机(prop.m_UI->m_部件->m_父部件);
	if (m) {
		camera->m_投影模式 = E_投影模式::e_透视;
	}
	else {
		camera->m_投影模式 = E_投影模式::e_正交;
	}
	f_widget_更新绘制(view);
}


static void on_场景物体变换更新(S_Props& prop) {
	if (f_prop_Bool(S_节点数据状态::g_是否打开了编辑模式)) {
		for (auto& ob : S_节点数据::g_编辑物体) {
			ob->m_isUpdate = true;
		}
	}
	else {
		for (auto& ob : S_框架::g_拾取物体) {
			ob->m_isUpdate = true;
		}
		f_物体约束_更新拾取物体变换();

		S_框架::g_变换手柄属性.position = f_global_get激活物体包围中心点();
		f_view3d_置变换手柄变换(S_框架::g_变换手柄属性);


		for (auto& e : C_3D场景视口::gui_3D视口) {
			e->m_更新绘制属性 = true;
		}
	}
}



static E_事件是否传递 on_3D视口_开始拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件);
	
	if (!C_3D场景视口::g视口摄像机旋转 && C_3D场景视口::g视口摄像机平移 <= 1 && S_框架::g_操作变换轴 == E_轴向::e_None轴向) {
		g选框起点坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		g选框终点坐标 = g选框起点坐标;
		
		g打开框选 = true;
	}
	return E_事件是否传递::e_事件传递_继续;
}

E_事件是否传递 on_3D视口_拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件);

	if (g打开框选 && !C_3D场景视口::g视口摄像机旋转 && C_3D场景视口::g视口摄像机平移 <= 1 && S_框架::g_操作变换轴 == E_轴向::e_None轴向) {
		g选框终点坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		view.m_更新绘制属性 = true;

		
	}
	return E_事件是否传递::e_事件传递_继续;
}

static bool on_3D视口_结束拖放(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件);
	
	uint8 摄像机平移 = C_3D场景视口::g_移动视口_鼠标左键;
	
	if (!C_3D场景视口::g视口摄像机旋转 && C_3D场景视口::g视口摄像机平移 <= 1 && S_框架::g_操作变换轴 == E_轴向::e_None轴向) {
		auto rect = f_ui_get视口(view.ui_三维视口);
		//翻转Y轴方向
		g选框起点坐标.y = rect.extent.y - g选框起点坐标.y;
		g选框终点坐标.y = rect.extent.y - g选框终点坐标.y;

		auto posA = vec_Min(g选框起点坐标, g选框终点坐标);
		auto posB = vec_Max(g选框起点坐标, g选框终点坐标);

		auto size = posB - posA;
		if (size.x > 2 || size.y > 2) {
			auto camera = f_ui_get3D视口摄像机(view.ui_三维视口);
			
			vec3 眼睛 = f_camera_get眼睛位置(*camera);
			vec3 视线 = f_camera_get视线向量(*camera);

			//f_view3d_设置激活物体焦点(false);
			vec3 坐标 = { posA.x, posA.y, 0.00001f };

			vec3 射线[4][2] = { {} };
			f_scene_构建屏幕射线(坐标, *camera, rect, 射线[0]);

			坐标 = { posB.x, posA.y, 0.00001f };
			f_scene_构建屏幕射线(坐标, *camera, rect, 射线[1]);

			坐标 = { posB.x, posB.y, 0.00001f };
			f_scene_构建屏幕射线(坐标, *camera, rect, 射线[2]);
			
			坐标 = { posA.x, posB.y, 0.00001f };
			f_scene_构建屏幕射线(坐标, *camera, rect, 射线[3]);


			vec3 三角形[3] = { 射线[0][0], 射线[0][1], 射线[1][1] };
			vec4 plane上 = f_graph_顶点构建平面(三角形);

			三角形[0] = 射线[2][0];
			三角形[1] = 射线[2][1];
			三角形[2] = 射线[2][1] + (射线[0][1] - 射线[1][1]);
			vec4 plane下 = f_graph_顶点构建平面(三角形);

			三角形[0] = 三角形[2];
			三角形[1] = 射线[0][1];
			三角形[2] = 射线[0][0]; 
			vec4 plane左 = f_graph_顶点构建平面(三角形);

			三角形[0] = 射线[1][0];
			三角形[1] = 射线[1][1];
			三角形[2] = 射线[2][1];
			vec4 plane右 = f_graph_顶点构建平面(三角形);

			三角形[0] = 射线[0][0];
			三角形[1] = 射线[1][0];
			三角形[2] = 射线[2][0];
			vec4 plane近 = f_graph_顶点构建平面(三角形);


			vec4 近平面 = f_graph_坐标构建平面(眼睛, 眼睛);
			vec4 plane[5] = { plane上, plane下, plane左, plane右, plane近 };

			Mat44f 相机矩阵[2] = {camera->m_相机投影矩阵, camera->m_相机视图矩阵};

			std::set<S_物体*> 选中物体;
			for (auto& e : f_global_渲染物体()) {
				if (e.second->m_可拾取) {
					f_scene_框选物体(e.second, 选中物体, plane, 射线);
				}
			}

			f_om_设置拾取物体(选中物体, false, E_工作区类型::e_3D视口);


			if (选中物体.size()) {
				if (f_prop_Bool(S_节点数据状态::g_是否打开了编辑模式)) {
					f_NodeCtx_clear选中编辑物体();

					switch (S_框架::g当前编辑对象类型) {
						case E_物体类型::t_骨骼:
						case E_物体类型::t_网格物体: {
							break;
						}
						case E_物体类型::t_空间曲线: {
							for (auto& e : 选中物体) {
								f_NodeCtx_set选中编辑物体(e, 0, true);
							}
							break;
						}
						case E_物体类型::t_骨架: {
							for (auto& e : 选中物体) {
								switch (e->m_Type) {
									
									case E_物体类型::t_骨节:
										f_NodeCtx_set选中编辑物体(e, 0, true);
										break;
									case E_物体类型::t_骨架:
									case E_物体类型::t_骨骼:
										f_NodeCtx_set选中编辑物体(e, 1, true);
										break;
									case E_物体类型::t_虚拟体: {
										f_NodeCtx_set选中辅助编辑物体(e, 0, true);
										break;
									}
								}
							}
							break;
						}
						default:
							break;
					}
				}
				else {
					S_框架::g_标记变换更新后事件 = 2;

					f_view3d_重构属性栏(static_cast<C_工作区页面*>(view.m_父部件)->mui_属性栏, S_框架::g_拾取物体, view.ui_三维视口);
				}

			}

		}
	}

	g打开框选 = false;
	S_框架::g_操作变换轴 = E_轴向::e_None轴向;
	return E_事件是否传递::e_事件传递_继续;
}



static void on_视口操作图形层绘制(C_Widget* self, S_2D画布* 画布) {
	C_3D场景视口& box = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件);

	auto 背景 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充面, E_图层混合模式::e_Normal);
	auto 图标 = f_vg_genIco((*self), *画布, E_图层混合模式::e_Normal, E_填充模式::e_填充面, S_UI主题::ui_默认图标);
	auto 线框 = f_vg_genConvex((*self), *画布, E_填充模式::e_填充线, E_图层混合模式::e_Normal);
	auto 线段 = f_vg_genLine((*self), *画布, E_图层混合模式::e_Normal, E_线类型::e_连续线);
}

static void on_视口操作图形层变换(C_Widget* self) {
	C_3D场景视口& box = *(C_3D场景视口*)self->m_父部件->m_父部件;

	auto* 背景 = f_widget_get渲染层(self, 0);
	auto* 图标 = f_widget_get渲染层(self, 1);
	auto* 线框 = f_widget_get渲染层(self, 2);
	auto* 线段 = f_widget_get渲染层(self, 3);

	//float32 强度 = box.ui_坐标组件->m_Size.x * 0.5;
	//强度 = (强度 - DEF_Min(强度, box.m_方向小组件_鼠标距离中心距离)) / 强度;

	std::vector<S_GPU内存块*>	形状;
	std::vector<S_2DConvexRect>	区域;
	std::vector<S_2D颜色包>		颜色;
	std::vector<vec2>			位置;

	std::vector<S_GPU内存块*>	线框形状;
	std::vector<S_2DConvexRect>	线框区域;
	std::vector<S_2D颜色包>		线框颜色;
	std::vector<vec2>			线框位置;


	S_2DConvexRect rect{};
	rect.size = box.ui_透视模式组件->m_Size;
	rect.AA_type = {1,0};

	形状.emplace_back(&self->m_UIctx->m_Ctx.m_几何图形->m_平面圆形);
	位置.emplace_back(box.ui_透视模式组件->m_Pos);
	颜色.emplace_back(S_2D颜色包(S_UI主题::uic_菜单背景, 200, 0, 0));
	区域.emplace_back(rect);

	形状.emplace_back(&self->m_UIctx->m_Ctx.m_几何图形->m_平面圆形);
	位置.emplace_back(box.ui_移动视口组件->m_Pos);
	颜色.emplace_back(S_2D颜色包(S_UI主题::uic_菜单背景, 200, 0, 0));
	区域.emplace_back(rect);



	std::vector<std::u16string> 图标名称;
	std::vector<S_RGBA8UI>		图标颜色;
	std::vector<vec2>			图标位置;
	std::vector<vec2>			图标大小;

	图标名称.emplace_back(box.ui_透视模式组件->m_ICO);
	图标颜色.emplace_back(S_UI主题::uic_图标颜色);
	图标位置.emplace_back(box.ui_透视模式组件->m_Pos + rect.size.y * 0.5f);
	图标大小.emplace_back(_Vec2(rect.size.y * 0.7));

	图标名称.emplace_back(box.ui_移动视口组件->m_ICO);
	图标颜色.emplace_back(S_UI主题::uic_图标颜色);
	图标位置.emplace_back(box.ui_移动视口组件->m_Pos + rect.size.y * 0.5f);
	图标大小.emplace_back(_Vec2(rect.size.y * 0.7));



	if (g打开框选) {
		auto posA = vec_Min(g选框起点坐标, g选框终点坐标);
		auto posB = vec_Max(g选框起点坐标, g选框终点坐标);

		rect.size = posB - posA;
		
		区域.emplace_back(rect);
		形状.emplace_back(&self->m_UIctx->m_Ctx.m_几何图形->m_平面矩形);
		位置.emplace_back(posA);
		颜色.emplace_back(S_2D颜色包(S_主题颜色::uic_物体选框颜色, 0, 0, 0));
		
		线框区域.emplace_back(rect);
		线框形状.emplace_back(&self->m_UIctx->m_Ctx.m_几何图形->m_平面矩形);
		线框位置.emplace_back(posA);
		线框颜色.emplace_back(S_2D颜色包(g选框边颜色, 0, 0, 0));
	}

	f_vg_tranform(*背景, 位置.size(), 位置.data());
	f_vg_drawConvex(*背景, 形状.size(), 形状.data(), 区域.data());
	f_vg_color(*背景, 颜色.size(), 颜色.data());

	f_vg_tranform(*图标, 图标位置.size(), 图标位置.data(), 图标大小.data());
	f_vg_drawIco(*图标, 图标名称.size(), 图标名称.data());
	f_vg_color(*图标, 图标颜色.size(), 图标颜色.data());

	f_vg_tranform(*线框, 线框位置.size(), 线框位置.data());
	f_vg_drawConvex(*线框, 线框形状.size(), 线框形状.data(), 线框区域.data());
	f_vg_color(*线框, 线框颜色.size(), 线框颜色.data());


	std::vector<vec2> 直线坐标;
	f_vg_setLine(*线段, box.m_参考线坐标.size(), box.m_参考线坐标.data(), 2.0);
	f_vg_tranform(*线段, {});
	f_vg_color(*线段, 1, &S_UI主题::uic_暗黑);

}


static E_事件是否传递 on_方向小组件_准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);
	C_3D场景视口::g视口摄像机旋转 = true;
	data.m_Type = E_拖放数据类型::e_鼠标拖放_静态移动;
	f_widget_开始固定鼠标位置();
	return E_事件是否传递::e_事件传递_终止;
}

static E_事件是否传递 on_方向小组件_拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);

	if (data.m_Type == E_拖放数据类型::e_鼠标拖放_静态移动) {
		
		f_工作区_旋转摄像机(&view, E_键位执行状态::e_执行);

		f_widget_更新绘制(self);
		f_widget_固定鼠标位置();
		view.ui_操作图形层->m_更新绘制属性 = true;
	}

	return E_事件是否传递::e_事件传递_终止;
}

static bool on_方向小组件_结束拖放(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);
	C_3D场景视口::g视口摄像机旋转 = false;
	return E_事件是否传递::e_事件传递_终止;
}




static E_事件是否传递 on_移动视口_开始拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);
	data.m_Type = E_拖放数据类型::e_鼠标拖放_静态移动;
	data.m_Next = &view;
	S_框架::g_鼠标起始坐标 = f_view3d_get平面鼠标位置(&view);
	f_widget_开始固定鼠标位置();
	return E_事件是否传递::e_事件传递_继续;
}

E_事件是否传递 on_移动视口_拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	
	if (!C_3D场景视口::g视口摄像机旋转) {
		if (data.m_Type != E_拖放数据类型::e_鼠标拖放_静态移动) {
			E_事件是否传递::e_事件传递_继续;
		}
		C_3D场景视口& view = *static_cast<C_3D场景视口*>((C_Widget*)data.m_Next);

		auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);
		auto rect = view.f_getRect();

		auto move = C_Widget::g_鼠标.lPos;
		move.y *= -1;

		if (camera->m_投影模式 == E_投影模式::e_透视) {
			vec2 比例 = f_ui_get空间投影比例(rect, *camera);
			move.x /= 比例.x;
			move.y /= 比例.y;
		}
		else {
			move /= rect.extent.y;
			move *= camera->m_视距;
		}
		f_camera_set转盘视点增量平移(*camera, move);


		f_辅助物体_基准网格缩放(camera);
		f_view3d_缩放所有视口变换手柄();
		
		//f_widget_更新渲染层(view.ui_三维视口);
		

		f_widget_固定鼠标位置();
	}

	return E_事件是否传递::e_事件传递_终止;
}

static bool on_移动视口_结束拖放(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);
	C_3D场景视口::g视口摄像机旋转 = false;
	return E_事件是否传递::e_事件传递_继续;
}





static E_事件是否传递 on_方向小组件_鼠标位置改变(C_Widget* self, const ivec4& mouse) {
	static vec2 轴线 = { 1,0 };
	
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self->m_父部件->m_父部件->m_父部件);
	C_工作区页面* page = static_cast<C_工作区页面*>(view.m_父部件);
	auto* camera = f_ui_get3D视口摄像机(self);
	auto rect = f_ui_get视口(self);
	
	if (!C_3D场景视口::g视口摄像机旋转) {
		auto pos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		vec3 坐标 = { pos.x, pos.y, 0.001f };

		view.m_方向小组件_鼠标距离中心距离 = vec_len(pos - rect.extent * 0.5);
		
		float32 a = 1 - min(vec_len(pos - rect.extent * 0.5) / (rect.extent.x * 0.5), 1);

		self->m_ColorA.m_颜色.a = 1 + min(a*2, 1) * 100;
		self->m_ColorA.m_模糊 = 200;
		self->m_更新绘制属性 = true;

	}

	view.ui_三维视口->m_颜色A = {25,38,50,255};
	view.m_更新绘制属性 = true;
	
	return E_事件是否传递::e_事件传递_终止;
}

static E_事件是否传递 on_方向小组件_鼠标离开(C_Widget* self, const S_鼠标& 鼠标) {
	C_3D场景视口& view = *(C_3D场景视口*)self->m_父部件->m_父部件->m_父部件;
	view.m_方向小组件_鼠标距离中心距离 = self->m_Size.x + 1;
	view.ui_操作图形层->m_更新绘制属性 = true;
	view.m_方向小组件_选中的ID = -1;

	self->m_ColorA.m_颜色.a = 20;
	self->m_ColorA.m_模糊 = 200;
	
	view.m_更新绘制属性 = true;
	return E_事件是否传递::e_事件传递_继续;
}


static void on_3D视口布局(C_Widget* self, C_Widget** ws, uint32 num) {
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self);
	float32 dpi = self->m_UIctx->m_DPI;

	ws[0]->f_setSize(self->m_Size);
	view.ui_操作图形层->f_setSize(self->m_Size);

	view.ui_坐标组件->f_setSize(vec2{ 100,100 } * dpi);
	view.ui_坐标组件->f_setPos(vec2{ self->m_Size.x - 104 * dpi, self->m_Size.y - 104 * dpi });

	S_框架::g_场景方向轴->m_网格模型[0].m_Mesh->m_线宽 = 3 * dpi;
	S_框架::g_场景方向轴->m_网格模型[1].m_Mesh->m_线宽 = dpi;


	vec2 pos = vec2{ self->m_Size.x - 34 * dpi, self->m_Size.y - 150 * dpi };
	view.ui_移动视口组件->f_setSize(_Vec2(30) * dpi);
	view.ui_移动视口组件->f_setPos(pos);

	pos.y -= 40 * dpi;
	view.ui_透视模式组件->f_setSize(_Vec2(30) * dpi);
	view.ui_透视模式组件->f_setPos(pos);
	
}

C_3D场景视口::C_3D场景视口(S_UI渲染环境& ctx) : C_Widget(&ctx) {
	mf_布局 = on_3D视口布局;
	
	ui_坐标组件 = f_ui_创建3D视口(S_框架::g_小组件场景, { 128, 128 }, ctx, *S_节点框架::g_3D视口环境);
	ui_三维视口 = f_ui_创建3D视口(f_NodeCtx_get默认场景(), {256,256}, ctx, *S_节点框架::g_3D视口环境);
	S_框架::g_视口相机.insert(f_ui_get3D视口摄像机(ui_三维视口));


	ui_操作图形层 = f_ui_创建组件(&ctx);
	ui_操作图形层->mf_绘制 = on_视口操作图形层绘制;
	ui_操作图形层->mf_变换 = on_视口操作图形层变换;
	
	ui_坐标组件->m_WName = u"坐标组件";
	ui_三维视口->m_WName = u"三维视口";
	f_widget_添加绘制画布(ui_操作图形层);



	ui_透视模式组件 = f_ui_创建开关(ctx);
	f_ui_置开关图标(ui_透视模式组件, {u"透视", u"正交"});
	m_透视模式 = f_alloc_BoolProp(nullptr, u"透视模式", true);
	m_透视模式.m_UI->m_Update = on_切换视口透视模式;
	m_透视模式.m_UI->m_部件 = ui_操作图形层;
	ui_透视模式组件->f_bind属性(m_透视模式);

	ui_移动视口组件 = new C_Widget(&ctx);
	ui_移动视口组件->m_ICO = u"移动";
	ui_移动视口组件->mf_开始拖拽 = on_移动视口_开始拖动;
	ui_移动视口组件->mf_拖拽     = on_移动视口_拖动;
	ui_移动视口组件->mf_结束拖拽 = on_移动视口_结束拖放;



	ui_透视模式组件->m_WName = u"透视模式组件";
	ui_移动视口组件->m_WName = u"移动视口组件";
	ui_操作图形层->m_WName = u"操作图形层";

	f_添加子组件(ui_三维视口);

	ui_操作图形层->f_添加子组件(ui_坐标组件);
	ui_操作图形层->f_添加子组件(ui_透视模式组件);
	ui_操作图形层->f_添加子组件(ui_移动视口组件);
	


	ui_三维视口->f_添加子组件(ui_操作图形层);
	ui_三维视口->mf_鼠标进入 = on_Widget鼠标进入;
	ui_三维视口->m_颜色A = {25,38,50,255};
	ui_三维视口->m_ColorA = S_2D颜色包({25,38,50,255}, 0, 0, 0);

	f_ui_3D视口添加渲染层(ui_三维视口, {0.0,0.0,0.0,0.0}, DEF_2DShaderType_AlphaStroke);
	f_ui_3D视口添加渲染层(ui_三维视口, {0.0,0.0,0.0,0.0}, 0);

	f_ui_3d视口清除颜色(ui_三维视口, { 0.2, 0.25, 0.3, 0 });
	f_ui_绑定3D视口渲染回调(ui_三维视口, {f_绘制_3D视口渲染回调, nullptr, nullptr, nullptr, uint64(this) });

	f_ui_3d视口清除颜色(ui_坐标组件, { 0.0, 0.0, 0.0, 0 });
	f_ui_绑定3D视口渲染回调(ui_坐标组件, {f_绘制_3D视口小组件渲染回调, nullptr, nullptr, nullptr, uint64(this) });

	

	S_3D视口属性& 视口属性 = f_ui_get3D视口渲染属性(ui_三维视口);
	视口属性.m_开启后期 = true;
	视口属性.m_调试层 = 3;
	视口属性.m_渲染模式 = 0;
	视口属性.m_渲染元素 = 3;

	m_物体显示模式 = f_alloc_FlagBitsEnumProp(&视口属性.m_渲染元素, *S_框架::g_视口物体显示模式项, u"显示模式", 3);
	m_物体显示模式.m_UI->m_Update = on_3D视口物体全局显示模式;
	m_物体显示模式.m_私有 = true;



	ui_坐标组件->m_颜色A		= {255,255,255, 1};
	ui_坐标组件->m_ColorA		= S_2D颜色包({255,255,255, 20}, 60, 0, 0);
	ui_坐标组件->mf_鼠标移动	= on_方向小组件_鼠标位置改变;
	ui_坐标组件->mf_鼠标离开	= on_方向小组件_鼠标离开;
	ui_坐标组件->m_图元形状		= &m_UIctx->m_Ctx.m_几何图形->m_平面圆形;
	
	ui_坐标组件->mf_开始拖拽	= on_方向小组件_准备拖动;
	ui_坐标组件->mf_拖拽		= on_方向小组件_拖动;
	ui_坐标组件->mf_结束拖拽	= on_方向小组件_结束拖放;

	auto* 小组件摄像机			= f_ui_get3D视口摄像机(ui_坐标组件);
	小组件摄像机->m_视距		= 5;
	小组件摄像机->m_近远端		= {0.01f, 10.0f};
	小组件摄像机->m_视角		= 35;


	ui_属性栏 = nullptr;
	ui_状态条 = nullptr;


	m_方向小组件_鼠标距离中心距离 = 128;
	m_是否开启视线旋转物体 = false;
	m_旋转度 = 0.0f;
	
	m_参考线坐标.resize(2);
	ui_操作图形层->mf_文件处理回调 = on_文件拖放_导入模型;


	m_变换手柄 = f_辅助物体_创建移动变换手柄(*S_节点框架::g_3D视口环境);
	S_框架::g_视口操作手柄[uint64(this)].push_back(m_变换手柄);



	m_数值输入显示 = f_alloc_LineStrProp(nullptr, u"变换:", u"");
	m_数值输入显示.m_私有 = true;

	gui_3D视口.insert(this);
}

C_3D场景视口::~C_3D场景视口() {
	f_prop_Release(m_透视模式);
	f_prop_Release(m_数值输入显示);
	f_prop_Release(m_物体显示模式);

	auto& 场景辅助物体 = S_框架::g_视口操作手柄[uint64(this)];
	auto it = std::find(场景辅助物体.begin(), 场景辅助物体.end(), m_变换手柄);
	if (it != 场景辅助物体.end()) {
		场景辅助物体.erase(it);
	}
	
	gui_3D视口.erase(this);
}

float32 C_3D场景视口::f_旋转辅助线绘制(vec2 鼠标开始位置, vec2 鼠标当前位置) {
	static const vec2 轴线 = { 1,0 };

	vec2 鼠标移动方向 = (鼠标当前位置 - 鼠标开始位置);

	float32 旋转弧度 = f_graph_两向量夹角弧度(vec_normalize2(鼠标移动方向), 轴线);
	if (鼠标移动方向.y < 0) 旋转弧度 = M_PI - 旋转弧度 + M_PI;
	float32 长度 = vec_len(鼠标移动方向);


	auto rot = f_mat2_build(-旋转弧度);

	m_参考线坐标[0] = 鼠标开始位置;
	m_参考线坐标[1] = 鼠标开始位置 + rot * vec2{长度,0};
	
	m_参考线坐标[0].y = m_Size.y - m_参考线坐标[0].y;
	m_参考线坐标[1].y = m_Size.y - m_参考线坐标[1].y;
	return 旋转弧度;
}

float32 C_3D场景视口::f_缩放辅助线绘制(vec2 鼠标开始位置, vec2 鼠标当前位置) {
	static const vec2 轴线 = { 1,0 };

	vec2 鼠标移动方向 = (鼠标当前位置 - 鼠标开始位置);

	float32 旋转弧度 = f_graph_两向量夹角弧度(vec_normalize2(鼠标移动方向), 轴线);
	if (鼠标移动方向.y < 0) 旋转弧度 = M_PI - 旋转弧度 + M_PI;
	float32 长度 = vec_len(鼠标移动方向);


	auto rot = f_mat2_build(-旋转弧度);

	m_参考线坐标[0] = 鼠标开始位置;
	m_参考线坐标[1] = 鼠标开始位置 + rot * vec2{长度,0};
	
	m_参考线坐标[0].y = m_Size.y - m_参考线坐标[0].y;
	m_参考线坐标[1].y = m_Size.y - m_参考线坐标[1].y;
	return 长度;
}

void C_3D场景视口::f_关闭虚拟线绘制() {
	m_参考线坐标[0] = {};
	m_参考线坐标[1] = {};
	return;
}

void C_3D场景视口::f_设置渲染方式(uint8 id) {
	f_ui_切换3D视口渲染模式(ui_三维视口, E_物体渲染模式(id));
}

void C_3D场景视口::f_更新方向组件(const S_摄像机& camera) {
	auto* 坐标组件摄像机 = f_ui_get3D视口摄像机(ui_坐标组件);
	坐标组件摄像机->m_变换.rotation = camera.m_变换.rotation;
	ui_坐标组件->m_更新绘制属性 = true;
}



Inline vec2 f_场景视口说明信息框_高度计算(uint32 行数量, float32 dpi) {
	return {512 * dpi,dpi*26*行数量 + dpi*40};
}

static void on_场景视口说明信息框布局(C_Widget* self, C_Widget** w, uint32 num) {
	float32 dpi = self->m_UIctx->m_DPI;
	uint32 strNum = f_ui_取弹出消息框文本行数量(self);
	self->f_setSize(f_场景视口说明信息框_高度计算(strNum, dpi));
}

static void on_场景视口说明信息框变换(C_Widget* self) {
	auto* 背景 = f_widget_get渲染层(self, 0);
	auto* 字符 = f_widget_get渲染层(self, 1);

	float32 dpi = self->m_UIctx->m_DPI;


	std::vector<S_2DConvexRect>	区域;
	std::vector<S_GPU内存块*>	形状;
	std::vector<S_2D颜色包>		颜色;
	std::vector<vec2>			位置;

	std::vector<std::u16string> 标题字符;
	std::vector<S_Rect2Df>		标题区域;
	std::vector<S_RGBA8UI>		标题颜色;


	S_2DConvexRect rect{};
	rect.size = self->m_Size;
	rect.radius = {dpi*4,dpi*4};
	区域.emplace_back(rect);
	形状.emplace_back(&self->m_UIctx->m_Ctx.m_几何图形->m_平面上圆角矩形);
	颜色.emplace_back(self->m_ColorA);
	位置.emplace_back(vec2{});

	f_vg_drawConvex(*背景, 区域.size(), 形状.data(), 区域.data());
	f_vg_tranform(*背景, 位置.size(), 位置.data());
	f_vg_color(*背景, 颜色.size(), 颜色.data());


	uint32 strNum = f_ui_取弹出消息框文本行数量(self);
	float32 width[4] = {dpi*60, dpi*100, dpi*80, dpi*200};
	for (uint32 i = 0; i < strNum; ++i) {
		auto& 字符组 = f_ui_取弹出消息框文本(self, i);

		vec2 pos = {dpi*20, dpi*20 + dpi*26.0f*i};
		uint32 offsetIndex = 0;
		for (auto& e : 字符组) {
			标题字符.emplace_back(e);
			标题区域.push_back({ pos, {width[offsetIndex], 26 * dpi}});
			标题颜色.push_back({5,5,5,255});
			pos.x += width[offsetIndex];
			++offsetIndex;
		}
	}
	

	f_vg_drawString(*字符, 标题字符, 标题区域, 标题颜色, E_对齐方式::e_左对齐, {S_UI主题::ui_文字间隔, E_方向::e_横向 });
}

static void f_打开场景视口说明(S_Props& prop) {
	auto* w = prop.m_UI->m_部件;
	S_框架::g_ui_帮助弹出窗口->mf_布局 = on_场景视口说明信息框布局;
	S_框架::g_ui_帮助弹出窗口->mf_变换 = on_场景视口说明信息框变换;

	std::vector<std::vector<std::u16string>> 说明消息;
	
	auto t = C_键位映射配置::f_取3D视口快捷键名称字符(u"键盘移动视口");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"鼠标移动视口");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"推拉摄像机");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"旋转视口");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"切换摄像机透射模式");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"移动到激活物体中心");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);


	t[0] = u"快捷键:";
	t[1] = u"摄像机视图";
	t[2] = u"数字键盘";
	t[3] = u"左:3 右:6 上:8 下:2 前:1 后:3";
	说明消息.push_back(t);

	t[0] = u"快捷键:";
	t[1] = u"锁定变换";
	t[2] = u"键盘按键";
	t[3] = u"X轴:x Y轴:y Z轴:z 按XYZ 锁定对应轴";
	说明消息.push_back(t);
	
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"物体单选");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"物体加选");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"移动物体");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"旋转物体");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"复制物体");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);
	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"删除物体");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"打开物体添加菜单");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"反选物体");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"框选排除");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);

	t = C_键位映射配置::f_取3D视口快捷键名称字符(u"框选");
	t.insert(t.begin(), u"快捷键:");
	说明消息.push_back(t);


	float32 dpi = w->m_UIctx->m_DPI;
	vec2 offset = f_场景视口说明信息框_高度计算(说明消息.size(), dpi);
	offset.y = prop.m_UI->m_部件->m_GlobalLoc.y - offset.y;
	offset.x = prop.m_UI->m_部件->m_GlobalLoc.x;
	//vec2 offset = dynamic_cast<C_工作区页面*>(w->m_父部件->m_父部件)->mui_视口页面->m_GlobalLoc;
	f_ui_弹出消息框置文本(S_框架::g_ui_帮助弹出窗口, 说明消息);
	f_ui_打开弹出窗口(S_框架::g_ui_帮助弹出窗口, offset);
}

void f_view3d_重构状态条(C_状态条* 状态条, C_Widget* 三维视口) {
	C_3D场景视口& box = *static_cast<C_3D场景视口*>(三维视口->m_父部件);
	S_3D视口属性& 视口属性 = f_ui_get3D视口渲染属性(三维视口);

	
	std::vector<S_Props> props;
	S_Props	prop;


	prop = f_alloc_OpsProp(f_打开场景视口说明, u"场景视口说明", u"疑问");
	prop.m_风格属性->m_图元形状 = &状态条->m_UIctx->m_Ctx.m_几何图形->m_平面圆形;
	prop.m_布局方式 = E_布局方式::e_等比;
	props.push_back(prop);


	static std::vector<S_标题和图标> 变换模式标题和图标 = {
		{u"万向",	u"删除"},
		{u"移动",	u"移动"},
		{u"旋转",	u"旋转"},
		{u"缩放",	u"缩放2"},
	};
	prop = f_alloc_EnumProp((int32*)&S_框架::g_变换手柄类型, 变换模式标题和图标, u"变换模式", S_框架::g_变换手柄类型);
	prop.m_UI->m_Update = on_3D视口物体变换模式;
	props.push_back(prop);

	switch (S_框架::g当前编辑对象类型) {
		case E_物体类型::t_网格物体:
		case E_物体类型::t_多边形:
		case E_物体类型::t_空间曲线: {
			props.push_back(S_节点数据状态::g_网格编辑模式);
			break;
		}
		
		case E_物体类型::t_骨架: {
			props.push_back(S_节点数据状态::g_骨架编辑模式);
			S_节点数据状态::g_骨架编辑模式.m_UI->m_Update = on_骨骼编辑操作模式切换;

			switch (f_prop_enum(S_节点数据状态::g_骨架编辑模式)) {
				case E_骨骼编辑模式::e_骨架模式: {
					break;
				}
				case E_骨骼编辑模式::e_编辑模式: 
					///{
					///	props.push_back(S_节点数据状态::g_骨骼编辑模式);
					///	S_节点数据状态::g_骨骼编辑模式.m_UI->m_Update = f_OE_骨骼姿态编辑模式切换;
					///	break;
					///}
				case E_骨骼编辑模式::e_姿态模式: {
					props.push_back(S_节点数据状态::g_骨骼编辑模式);
					S_节点数据状态::g_骨骼编辑模式.m_UI->m_Update = f_OE_骨骼姿态编辑模式切换;
					break;
				}
			}
			break;
		}
			
		case E_物体类型::t_灯光:
			props.push_back(S_节点数据状态::g_灯光编辑模式);
			break;
		default:
			break;
	}
	


	prop = f_alloc_NoneProp();
	props.push_back(prop);

	props.push_back(box.m_物体显示模式);

	prop = f_alloc_NoneProp();
	props.push_back(prop);

	prop = f_alloc_EnumProp(&视口属性.m_渲染模式,
							{
								{u"单色", u"单面"}, {u"颜色ID", u"双面"} , {u"光照", u"光照"}
							}, u"渲染预览模式");
	prop.m_UI->m_Update = on_3D视口_面渲染模式;
	props.push_back(prop);

	prop = f_alloc_NoneProp();
	props.push_back(prop);


	props.push_back(box.m_数值输入显示);


	//if (S_节点数据::g_物体全局渲染模式 == 4) {
		prop = f_alloc_BoolProp(&视口属性.m_开启调试, u"开启调试");
		prop.m_UI->m_Update = f_开启视口调试_状态栏更新;
		//props.push_back(prop);

		if (视口属性.m_开启调试) {
			prop = f_alloc_F32Prop(&视口属性.m_调试占比, u"显示占比");
			//props.push_back(prop);

			prop = f_alloc_EnumProp(&视口属性.m_调试层方向,
				{
					{u"横向", u"横向排列"},
					{u"纵向", u"纵向排列"} ,
				},
				u"渲染预览模式"
				);
			//props.push_back(prop);

			//prop = f_alloc_EnumProp(&视口属性.m_调试层方向,
			//	{
			//		{u"合成", "明暗"}, {u"颜色", "棋盘格"} , {u"深度", "深度"} , {u"法线", "平面法线"}
			//	}, 
			//	u"渲染预览模式"
			//);
			//props.push_back(prop);
		}
	//}


	状态条->f_构建属性部件(props, 状态条);
}

void f_view3d_重构属性栏(C_属性栏* plane, const std::set<S_物体*>& obs, C_Widget* 三维视口) {
	std::vector<S_Props> props;

	if (obs.size() == 1) {
		S_物体* m_Ob = *obs.begin();

		static vec3* 位置p = nullptr;
		static vec3* 旋转p = nullptr;
		static vec3* 缩放p = nullptr;
		
		位置p = &m_Ob->m_变换.position;
		旋转p = &m_Ob->m_变换.rotation;
		缩放p = &m_Ob->m_变换.scale;

		S_Props 位置 = f_alloc_Vec3Prop(&m_Ob->m_变换.position, u"位置");
		S_Props 旋转 = f_alloc_Vec3Prop(&m_Ob->m_变换.rotation, u"旋转");
		S_Props 缩放 = f_alloc_Vec3Prop(&m_Ob->m_变换.scale, u"缩放");

		位置.m_私有 = true;										
		旋转.m_私有 = true;										
		缩放.m_私有 = true;	

		位置.m_UI->m_Update = on_场景物体变换更新;
		旋转.m_UI->m_Update = on_场景物体变换更新;
		缩放.m_UI->m_Update = on_场景物体变换更新;
		props.push_back(位置);
		props.push_back(旋转);
		props.push_back(缩放);			
		

		S_Props prop = f_alloc_NoneProp();
		props.push_back(prop);

		//if (三维视口->m_UIctx->m_Ctx.m_支持光追) {
		//	prop = f_alloc_FlagBitsEnumProp(&m_Ob->m_视口显示模式, *S_节点数据::g_物体显示模式项_光追, u"显示模式", m_Ob->m_视口显示模式);
		//	prop.m_UI->m_Update = on_场景物体显示方式;
		//}
		//else {
		//	prop = f_alloc_FlagBitsEnumProp(&m_Ob->m_视口显示模式, *S_框架::g_视口物体渲染模式项, u"显示模式", m_Ob->m_视口显示模式);
		//	prop.m_UI->m_Update = on_场景物体显示方式;
		//}
		//props.push_back(prop);

		prop = f_alloc_Object(&S_框架::g_物体节点, u"物体节点");
		if (m_Ob->m_扩展属性[0]) {
			f_prop_ObjectMapSelect(prop, m_Ob->m_扩展属性[0]->m_Name);
		}
		prop.m_UI->m_Update = f_NODE_物体节点名称修改;
		props.push_back(prop);
		

		prop = f_alloc_NoneProp();
		props.push_back(prop);

		switch (f_ob_get附属类型(m_Ob)) {
			case E_物体类型::t_摄像机: {
				auto* 摄像机 = f_ob_以类型查找子物体(m_Ob, E_物体类型::t_摄像机);
				if (摄像机) {
					S_Props 相机旋转 = f_alloc_Vec3Prop(&摄像机->m_变换.rotation, u"相机旋转");
					相机旋转.m_UI->m_Update = on_场景物体变换更新;
					props.push_back(相机旋转);
				}
				break;
			}
			case E_物体类型::t_灯光: {
				
				auto* lp = f_ob_getLigth(f_ob_get子对象(m_Ob, 0));

				break;
			}
		}

		
		//std::vector<C_节点基类*> nodes = f_NodeCtx_从可视物体取节点(obs);
		//for (auto& node : nodes) {
		//	if (node->m_包含在多个视口的UI部件.size()) {
		//		C_节点面板* 节点面板 = (C_节点面板*)(*node->m_包含在多个视口的UI部件.begin());
		//		if (节点面板->m_面板属性构建) {
		//			节点面板->m_面板属性构建(node, props);
		//		}
		//	}
		//}
	}
	else if (obs.size() > 1) {
		S_Props prop;
		static vec3 位置 = {};
		static vec3 旋转 = {};
		static vec3 缩放 = {};
		位置 = {};
		旋转 = {};
		缩放 = {};
		prop = f_alloc_Vec3Prop(&位置, u"多选物体位置");
		props.push_back(prop);
		prop = f_alloc_Vec3Prop(&旋转, u"多选物体旋转");
		props.push_back(prop);
		prop = f_alloc_Vec3Prop(&缩放, u"多选物体缩放");
		props.push_back(prop);


		prop = f_alloc_NoneProp();
		props.push_back(prop);

		//uint32 多选物体显示模式 = 0;
		//prop = f_alloc_Prop多按钮(&多选物体显示模式, *S_节点数据::g_物体显示模式项_光追, u"打开显示", 多选物体显示模式);
		//prop.m_UI->m_Update = on_场景打开选择的多物体显示方式;
		//props.push_back(prop);
		//
		//prop = f_alloc_Prop多按钮(&多选物体显示模式, *S_节点数据::g_物体显示模式项_光追, u"关闭显示", 多选物体显示模式);
		//prop.m_UI->m_Update = on_场景关闭选择的多物体显示方式;
		//props.push_back(prop);
		
	}

	plane->f_构建属性部件(props);
}

void f_view3d_更新属性栏() {
	for (auto& e : C_3D场景视口::gui_3D视口) {
		C_工作区页面* page = static_cast<C_工作区页面*>(e->m_父部件);
		page->mui_属性栏->m_更新绘制属性 = true;
		page->mui_属性栏->f_属性更新到组件();
	}
}




bool f_view3d_射线选中物体(const vec3& s, const vec3& dir, bool 多选物体, S_摄像机& camera, const S_Rect2Df& rect, const vec2& 鼠标位置) {
	
	float32 最小距离 = 1.0e+8;
	S_物体* 拾取物体 = nullptr;

	vec3 视线起点方向相机位置[3] = {s, dir};
	for (auto& e : f_global_渲染物体()) {
		if (e.second->m_可拾取) {
			auto mxx = f_mat4_identity();

			mxx.m3 = f_bm_at<mat4>(e.second->m_变换矩阵).m3;
			mxx.m3.w = 1;

			vec3 最小点 = (*(Mat44f*)&mxx) * (e.second->m_包围盒.pos - e.second->m_包围盒.size);
			vec3 最大点 = (*(Mat44f*)&mxx) * (e.second->m_包围盒.pos + e.second->m_包围盒.size);


			bool isPick = f_intersect_RayAndAABB((float32*)&s, (float32*)&dir, (float32*)&最小点, (float32*)&最大点);
			if (isPick) {
				
				float32 d = f_scene_拾取物体网格(e.second, 0, 视线起点方向相机位置);
				if (d >= 0 && d < 最小距离) {
					最小距离 = d;
					拾取物体 = e.second;
				}
			}
		}
	}

	if (拾取物体 && 最小距离 < 1.0e+8) {
		拾取物体->m_选中 = true;

		f_om_设置拾取物体({拾取物体}, 多选物体, E_工作区类型::e_3D视口);
	}
	
	return S_框架::g_拾取物体.size();
}

bool f_view3d_射线选中网格元素(const vec3& s, const vec3& e, bool 多选物体, S_摄像机& camera) {
	return false;
}

bool f_view3d_射线选中骨骼元素(const vec3& s, const vec3& e, bool 多选物体, S_摄像机& camera) {
	
	float32 最小距离 = 100000000;

	vec3 视线起点方向相机位置[3];
	视线起点方向相机位置[0] = s;
	视线起点方向相机位置[1] = vec_normalize2(vec_sub(e, s));
	视线起点方向相机位置[2] = f_camera_get眼睛位置(camera);
	
	std::set<S_物体*> 新拾取的物体;
	uint32 骨头元素ID = 0;
	
	for (auto& ob : S_框架::g_拾取物体) {
		uint32 网格槽ID[2] = { 0, 2 };
		
		for (auto& 骨骼 : ob->m_子物体) {
			f_scene_拾取包围盒物体(新拾取的物体, static_cast<S_物体*>(骨骼), 视线起点方向相机位置);
		}

		S_物体拾取信息 拾取信息 = { nullptr, -1, 100000000 };
		for (auto& e : 新拾取的物体) {
			switch (f_prop_enum(S_节点数据状态::g_骨架编辑模式)) {
				case E_骨骼编辑模式::e_编辑模式: {
					if (e->m_Type == E_物体类型::t_骨骼 && f_ob_from骨骼(e)->m_对称物体) {
						continue;
					}
					break;
				}

				case E_骨骼编辑模式::e_骨架模式: { break; }
				case E_骨骼编辑模式::e_姿态模式: { break; }
			}

			uint32 槽数量 = e->m_DataNum > 2 ? 2 : 1;
			for (uint32 k = 0; k < 槽数量; ++k) {
				S_物体拾取信息 当前拾取 = f_scene_拾取物体网格(e, 网格槽ID[k], 视线起点方向相机位置, camera);
				if (当前拾取.相交深度 < 拾取信息.相交深度) {
					拾取信息 = 当前拾取;
				}
			}
		}

		
		if (拾取信息.物体) {
			switch (拾取信息.物体->m_Type) {
				case E_物体类型::t_实例:
					if(!拾取信息.物体->m_父对象 || static_cast<S_物体*>(拾取信息.物体->m_父对象)->m_Type != E_物体类型::t_骨节) continue;
				case E_物体类型::t_骨架:
				case E_物体类型::t_骨节:
				case E_物体类型::t_骨骼:
					f_NodeCtx_set选中编辑物体(拾取信息.物体, 拾取信息.实例ID, 多选物体);
					break;
				case E_物体类型::t_虚拟体: {
					f_NodeCtx_set选中辅助编辑物体(拾取信息.物体, 拾取信息.实例ID, 多选物体);
					break;
				}
			}
		}
	}

	
	
	f_camera_set偏移视点(camera, f_global_get激活物体包围中心点());

	f_工作区_联动更新(E_工作区类型::e_3D视口, E_工作区操作类型::e_拾取物体);
	return false;
}

bool f_曲线端点索引大小排序(uvec2& a, uvec2& b) {
	return ((uint64(a.x) << 32) | (a.y)) > ((uint64(b.x) << 32) | (b.y));
}
bool f_view3d_射线选中曲线元素(const vec2& 鼠标位置, bool 多选物体, Mat44f 相机矩阵[3], const S_Rect2Df& rect, vec3& 中点) {
	auto temp = S_框架::g_选择端点;

	if(!多选物体) temp.clear();
	
	for (auto ob : S_节点数据::g_编辑物体) {
		Mat44f mat[3] = { f_ob_get变换矩阵(ob, ob->m_实例ID), 相机矩阵[0], 相机矩阵[1]};

		uvec2 index = f_surface_屏幕拾取曲线控制点(f_ob_getMesh(ob), mat, 鼠标位置, rect, 30);

		if (index.x != 0xffffffff) {
			temp[ob].push_back(index);

			//if (index.y == 0) {
			//	temp[ob].push_back({ index.x, 65537 });
			//	temp[ob].push_back({ index.x, 65538 });
			//}
		}
	}

	

	if (temp.size()) {
		S_框架::g_选择端点 = temp;

		for (auto& e : S_框架::g_选择端点) {
			sort(e.second.begin(), e.second.end(), f_曲线端点索引大小排序);

			auto it = unique(e.second.begin(), e.second.end(), [](const uvec2& a, const uvec2& b) {
				return ((uint64(a.x) << 32) | (a.y & 0x0000ffff)) == ((uint64(b.x) << 32) | (b.y & 0x0000ffff));
			});
			e.second.erase(it, e.second.end());
		}


		
		uint32 选中的点数量 = 0;
		for (auto& e : S_框架::g_选择端点) {
			auto* me = f_ob_getMesh(e.first);
			Mat44f m = f_ob_get变换矩阵(e.first, e.first->m_实例ID);
			

			auto point = f_buf_CurvePoint3D_ptr(*me->m_顶点);
			if (!多选物体) {
				uint32 num = me->m_顶点->m_Mem.m_数量;
				for (uint32 i = 0; i < num; ++i) {
					point[i].ID_Type_Color.y &= 0x0000ffff;
				}
			}


			for (auto& i : e.second) {
				uint32 mask = 0;
				switch (i.y) {
					case 0: {
						mask |= 1;

						中点 += m * point[i.x].value;
						break;
					}
					case 1: {
						mask |= 2;

						中点 += m * point[i.x].L_point;
						break;
					}
					case 2: {
						mask |= 4;

						中点 += m * point[i.x].R_point;
						break;
					}
					default:
						continue;
				}
				point[i.x].ID_Type_Color.y |= mask << 16;
				++选中的点数量;
			}
		}


		if (选中的点数量) {
			中点 /= 选中的点数量;
		}
		return true;
	}
	return false;
}

bool f_view3d_框选物体(C_3D场景视口& view, vec2 鼠标开始位置, vec2 鼠标结束位置, bool 反选, E_键位执行状态 阶段) {
	
	view.m_更新绘制属性 = true;
	//std::cout<<"g打开框选 "<<g打开框选 << " 阶段 " << int32(阶段) << std::endl;
	switch (阶段) {
		case (E_键位执行状态::e_开始): {
			g选框起点坐标 = 鼠标开始位置;
			g选框终点坐标 = 鼠标结束位置;

			g打开框选 = true;
			if (反选) {
				g选框边颜色 = {200,60,250,255};
			}
			else {
				g选框边颜色 = S_主题颜色::uic_物体选框边颜色;
			}
			return false;
		}
		case (E_键位执行状态::e_执行): {
			g选框终点坐标 = 鼠标结束位置;
			return false;
		}
		case (E_键位执行状态::e_完成): {
			g打开框选 = false;
			g选框边颜色 = S_主题颜色::uic_物体选框边颜色;
			break;
		}
	}
	
	auto rect = f_ui_get视口(view.ui_三维视口);


	//翻转Y轴方向
	鼠标开始位置.y = rect.extent.y - 鼠标开始位置.y;
	鼠标结束位置.y = rect.extent.y - 鼠标结束位置.y;

	auto posA = vec_Min(鼠标开始位置, 鼠标结束位置);
	auto posB = vec_Max(鼠标开始位置, 鼠标结束位置);

	auto size = posB - posA;
	if (size.x >= 2 || size.y >= 2) {
		auto camera = f_ui_get3D视口摄像机(view.ui_三维视口);

		vec3 眼睛 = f_camera_get眼睛位置(*camera);
		vec3 视线 = f_camera_get视线向量(*camera);

		vec3 坐标 = { posA.x, posA.y, 0.00001f };
		vec3 射线[4][2] = { {} };
		f_scene_构建屏幕射线(坐标, *camera, rect, 射线[0]);

		坐标 = { posB.x, posA.y, 0.00001f };
		f_scene_构建屏幕射线(坐标, *camera, rect, 射线[1]);

		坐标 = { posB.x, posB.y, 0.00001f };
		f_scene_构建屏幕射线(坐标, *camera, rect, 射线[2]);

		坐标 = { posA.x, posB.y, 0.00001f };
		f_scene_构建屏幕射线(坐标, *camera, rect, 射线[3]);



		vec3 三角形[3] = { 射线[0][0], 射线[0][1], 射线[1][1] };
		vec4 plane上 = f_graph_顶点构建平面(三角形);

		三角形[0] = 射线[2][0];
		三角形[1] = 射线[2][1];
		三角形[2] = 射线[2][1] + (射线[0][1] - 射线[1][1]);
		vec4 plane下 = f_graph_顶点构建平面(三角形);

		三角形[0] = 三角形[2];
		三角形[1] = 射线[0][1];
		三角形[2] = 射线[0][0]; 
		vec4 plane左 = f_graph_顶点构建平面(三角形);

		三角形[0] = 射线[1][0];
		三角形[1] = 射线[1][1];
		三角形[2] = 射线[2][1];
		vec4 plane右 = f_graph_顶点构建平面(三角形);

		三角形[0] = 射线[0][0];
		三角形[1] = 射线[1][0];
		三角形[2] = 射线[2][0];
		vec4 plane近 = f_graph_顶点构建平面(三角形);


		vec4 近平面 = f_graph_坐标构建平面(眼睛, 眼睛);
		vec4 plane[5] = { plane上, plane下, plane左, plane右, plane近 };

		Mat44f 相机矩阵[2] = {camera->m_相机投影矩阵, camera->m_相机视图矩阵};

		std::set<S_物体*> 选中物体;
		for (auto& e : f_global_渲染物体()) {
			if (e.second->m_可拾取) {
				f_scene_框选物体(e.second, 选中物体, plane, 射线);
			}
		}

		DEF_记录日志("框选:" + std::to_string(反选));
		if (选中物体.size()) {
			if (反选) {
				f_OM_排除拾取物体(选中物体, E_工作区类型::e_3D视口);
			}
			else {
				f_om_设置拾取物体(选中物体, false, E_工作区类型::e_3D视口);
			}
		}
		

		if (选中物体.size()) {
			f_view3d_重构属性栏(static_cast<C_工作区页面*>(view.m_父部件)->mui_属性栏, S_框架::g_拾取物体, view.ui_三维视口);
		}
	}
	

	S_框架::g_操作变换轴 = E_轴向::e_None轴向;
	return true;
}





bool f_view3d_物体编辑模式_键盘事件(const S_键盘& ks) {
	if (ks.Keys状态[S_InputKey::e_KEY_左SHIFT] == DEF_按键状态_按下中) {
		//vec2 g鼠标起始坐标 = f_widget_取鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		if (ks.Keys状态[S_InputKey::e_KEY_E] == DEF_按键状态_放开) {
			switch (f_prop_enum(S_节点数据状态::g_网格编辑模式)) {
			case 0: f_prop_enum(S_节点数据状态::g_网格编辑模式) = 1; break;
			case 1: f_prop_enum(S_节点数据状态::g_网格编辑模式) = 0; break;
			default:
				break;
			}
		}
	}

	return false;
}

bool f_view3d_骨骼编辑模式_键盘事件(C_Widget* self, const S_键盘& ks, S_UI渲染环境& ctx, S_摄像机* camera) {
	if (ks.Keys状态[S_InputKey::e_KEY_左SHIFT] == DEF_按键状态_按下中) {
		//vec2 g鼠标起始坐标 = f_widget_取鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		if (ks.Keys状态[S_InputKey::e_KEY_E] == DEF_按键状态_放开) {
			switch (f_prop_enum(S_节点数据状态::g_骨架编辑模式)) {
			case 0: 
				//f_prop_enum(S_节点数据状态::g_骨架编辑模式) = 1;
				//f_prop_Bool(S_节点数据状态::g_打开编辑模式) = true;
				break;
			case 1: 
				//f_prop_Bool(S_节点数据状态::g_打开编辑模式) = false;
				//f_prop_enum(S_节点数据状态::g_骨架编辑模式) = 0;
				break;
			default:
				break;
			}

			on_骨骼编辑操作模式切换(S_节点数据状态::g_骨架编辑模式);
			//f_view3d_骨骼状态切换(f_prop_enum(S_节点数据状态::g_骨骼编辑模式).m_当前选项);
			if (S_节点数据状态::g_骨架编辑模式.m_UI->m_Set) S_节点数据状态::g_骨架编辑模式.m_UI->m_Set(S_节点数据状态::g_骨架编辑模式);
		}

		return false;
	}
	
	return false;
}

void f_view3d_设置激活物体焦点(bool 激活) {
	if (激活) {
		for (auto* ob : S_框架::g_拾取物体) {
			f_global_物体显示模式_打开焦点(ob, ob->m_实例ID);
		}
	}
	else {
		for (auto* ob : S_框架::g_拾取物体) {
			f_global_物体显示模式_关闭焦点(ob, ob->m_实例ID);
		}
	}
}

bool f_view3d_创建物体菜单(C_Widget* self, const std::u16string name, uint16 id) {
	if (S_节点数据::g_物体节点树.size()) {
		C_节点树* t = (C_节点树*)(S_节点数据::g_物体节点树.front().m_Ptr);
	}
	

	DEF_3Fs 顶点;
	DEF_3Fs 法线;
	DEF_2Fs UV;
	std::vector<std::vector<uint32>> 索引;

	DEF_Es 边索引;
	std::vector<S_VNTC> vnData;
	std::vector<uint32> 新索引;

	//C_vectorGPU<S_VNTC> gpu顶点(S_节点框架::g_3D视口环境->m_几何图形->m_3DGPU缓存_顶点);
	//gpu顶点.resize(10000000);

	if(name == u"平面"){
		f_polygon_构建平面(vec2{1,1}, uvec2{1,1}, 顶点, 法线, UV, 索引);
	}
	if(name == u"球体"){
		f_polygon_构建球体(64, 32, 0.5, 顶点, 法线, UV, 索引);
	}
	if(name == u"圆环"){
		
	}
	if(name == u"方体"){
		f_polygon_构建方体(vec3{1,1,1}, uvec3{1,1,1}, 顶点, 法线, UV, 索引);
	}
	if(name == u"圆锥"){
		f_polygon_构建锥体(vec2{0.5,1}, 32, 顶点, 法线, UV, 索引);
	}
	if(name == u"圆柱"){
		f_polygon_构建圆柱(vec2{0.5,1}, {32, 1}, 顶点, 法线, UV, 索引);
	}
	f_surface_数组合并(vnData, 顶点, 法线, UV);


	S_物体* ob = f_ob_创建(*S_节点框架::g_3D视口环境, E_物体类型::t_多边形);
	f_OB_创建网格物体数据(*S_节点框架::g_3D视口环境, ob);


	auto& 边 = f_ob_get数据(ob, 1);
	边索引 = f_polygon_构建边(索引);
	f_me_fill索引(边.m_Mesh, 边索引);

	auto& 面 = f_ob_get数据(ob, 0);
	新索引 = f_polygon_三角化(vnData, 索引);
	f_me_fill顶点(面.m_Mesh, vnData);
	f_me_fill索引(面.m_Mesh, 新索引);


	

	C_3D场景视口* 三维视口 =  static_cast<C_3D场景视口*>(f_ui_从菜单项取发起组件(self));
	S_3D视口属性& 视口属性 = f_ui_get3D视口渲染属性(三维视口->ui_三维视口);
	switch (视口属性.m_渲染模式) {
		case 1: f_OB_置网格ID面材质(ob); break;
		case 2: f_OB_置网格光照面材质(ob); break;
		default: break;
	}
	f_OE_更新网格包围盒({ob});
	f_om_添加渲染物体({ob}, name);


	for (auto& e : C_3D场景视口::gui_3D视口) {
		C_工作区页面* page = static_cast<C_工作区页面*>(e->m_父部件);
		f_view3d_重构属性栏(page->mui_属性栏, {ob}, e->ui_三维视口);
	}
	f_工作区_联动更新(E_工作区类型::e_3D视口, E_工作区操作类型::e_创建物体);
	return false;
}

bool f_view3d_删除物体菜单(C_Widget* self, const std::u16string name, uint16 id) {
	if(name == u"删除"){
		f_om_删除渲染物体(S_框架::g_拾取物体);
	}
	return false;
}


void f_view3d_切换物体线显示() {
	
}

void f_view3d_缩放所有视口变换手柄() {
	for (auto& e : C_3D场景视口::gui_3D视口) {
		auto* camera = f_ui_get3D视口摄像机(e->ui_三维视口);
		f_辅助物体_变换手柄缩放(e->m_变换手柄, *camera, e->f_getRect());
	}
}

void f_view3d_置变换手柄变换(S_Tranform t) {
	Mat44f 父级变换 = f_mat44_identity();

	for (auto& e : C_3D场景视口::gui_3D视口) {
		if (e->m_显示) {
			e->m_变换手柄->m_变换 = t;
			e->m_变换手柄->m_isUpdate = true;

			f_ob_更新变换约束(e->m_变换手柄, &父级变换);
		}
	}
}



void f_3dView_置摄像机视点偏移(vec3 中心) {
	for (auto& e : C_3D场景视口::gui_3D视口) {
		auto* camera = f_ui_get3D视口摄像机(e->ui_三维视口);
		f_camera_set偏移视点(*camera, 中心);
	}
}

vec3 f_3dView_平面空间坐标(const S_摄像机& camera, vec2 屏幕坐标, vec3 平面偏移坐标, const S_Rect2Df& rect) {
	
	vec3 当前射线[2];
	vec3 射线方向 = f_scene_构建屏幕射线(_Vec3(屏幕坐标), camera, rect, 当前射线);
	vec3 视线向量 = normalize(当前射线[0] - 当前射线[1]);

	vec4 拾取平面 = f_graph_坐标构建平面(平面偏移坐标, 视线向量);
	vec3 交点{};
	f_graph_线段平面相交(当前射线[0], 当前射线[1], 拾取平面, 交点);

	return 交点;
}

vec2 f_3dView_屏幕坐标转相机偏移(const S_摄像机& camera, vec2 屏幕坐标, vec3 平面偏移坐标, const S_Rect2Df& rect) {
	
	vec3 当前射线[2];
	vec3 射线方向 = f_scene_构建屏幕射线(_Vec3(屏幕坐标), camera, rect, 当前射线);
	vec3 视线向量 = normalize(当前射线[0] - 当前射线[1]);

	vec4 拾取平面 = f_graph_坐标构建平面(平面偏移坐标, -视线向量);

	vec3 交点{};
	f_graph_线段平面相交(当前射线[0], 当前射线[1], 拾取平面, 交点);

	vec2 projCoordA = f_graph_plane_PlaneCoord(交点, 拾取平面, {0,1,0});
	return projCoordA;
}









E_事件是否传递 f_工作区_移动摄像机(C_Widget* self, E_键位执行状态 状态) {
	f_projectFile_设置文件保存状态(false);
	self->m_更新绘制属性 = true;

	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self);
	C_3D场景视口::g视口摄像机平移 |= C_3D场景视口::g_移动视口_鼠标左键;
	C_3D场景视口::g视口摄像机平移 |= C_3D场景视口::g_移动视口_键盘按键;

	auto rect = view.ui_三维视口->f_getRect();
	auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);

	S_框架::g_变换开关 = false;

	auto 局部位置 = f_view3d_get平面鼠标位置(view.ui_三维视口);
	vec2 move = 局部位置 - S_框架::g_鼠标起始坐标;

	if (camera->m_投影模式 == E_投影模式::e_透视) {
		vec2 比例 = f_ui_get空间投影比例(rect, *camera);
		move /= 比例;
	}
	else {
		move /= rect.extent.y;
		move *= camera->m_视距;
	}

	f_camera_set转盘视点平移(*camera, move);

	f_辅助物体_基准网格缩放(camera);
	f_view3d_缩放所有视口变换手柄();
	f_widget_更新绘制(self);
	return E_事件是否传递::e_事件传递_终止;
}


E_事件是否传递 f_工作区_旋转摄像机(C_Widget* self, E_键位执行状态 状态) {
	f_projectFile_设置文件保存状态(false);
	self->m_更新绘制属性 = true;
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self);


	C_3D场景视口::g视口摄像机旋转 = true;
	S_框架::g_变换开关 = false;

	f_ui_set3D视口默认摄像机(view.ui_三维视口);
	auto* camera_场景 = f_ui_get3D视口摄像机(view.ui_三维视口);
	auto* camera_方向 = f_ui_get3D视口摄像机(view.ui_坐标组件);

	
	vec3 up = camera_场景->m_旋转矩阵 * vec3{0,1,0};
	if (up.y >= 0) {
		up.x = C_Widget::g_鼠标.lPos.x * 0.7f;
	}
	else {
		up.x = C_Widget::g_鼠标.lPos.x * -0.7f;
	}
	up.y = C_Widget::g_鼠标.lPos.y * 0.7f;
	
	f_camera_set转盘旋转(*camera_场景, { up.y, up.x, 0.0f });
	camera_方向->m_变换.rotation = camera_场景->m_变换.rotation;

	f_辅助物体_基准网格缩放(camera_场景);
	f_view3d_缩放所有视口变换手柄();
	f_widget_更新绘制(self);
	f_widget_更新渲染层(view.ui_操作图形层);
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_工作区_推拉摄像机(C_Widget* self, E_键位执行状态 状态) {
	f_projectFile_设置文件保存状态(false);
	self->m_更新绘制属性 = true;
	C_3D场景视口& view = *static_cast<C_3D场景视口*>(self);

	if (f_ui_set3D视口是否为默认摄像机(view.ui_三维视口)) {
		auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);
		f_camera_scal转盘距离(*camera, -C_Widget::g_鼠标.lZ);

		f_辅助物体_基准网格缩放(camera);
		f_view3d_缩放所有视口变换手柄();
	}

	f_NodeCtx_get默认场景()->m_光追后端->f_重置渲染帧记录();
	f_widget_更新绘制(self);

	return E_事件是否传递::e_事件传递_终止;
}




bool f_3dView_物体数值输入(C_Widget* self, const int8 c, uint8 flag) {
	auto& view = *static_cast<C_3D场景视口*>(self);

	auto 物体移动变换 = [](float32 m){
		S_框架::g_变换手柄属性.position = S_射线拾取参数::g手柄初始变换;

		switch (S_框架::g_操作变换轴) {
			case E_轴向::e_X: {
				S_框架::g_变换手柄属性.position.x += m;
				break;
			}
			case E_轴向::e_Y: {
				S_框架::g_变换手柄属性.position.y += m;
				break;
			}
			case E_轴向::e_Z: {
				S_框架::g_变换手柄属性.position.z += m;
				break;
			}
			default:
				return;
		}
		f_变换_移动物体(S_框架::g_变换手柄属性.position);
		
	};

	auto 物体旋转变换 = [](float32 m, S_摄像机* camera){
		vec3 视线向量;
		int32 方向 = f_控制手柄_旋转视线向量(camera, 视线向量);
		float32 旋转弧度 = M_角度转弧度(m);
		
		S_框架::g_变换手柄属性 = f_控制手柄_视线旋转物体(视线向量, 旋转弧度, -方向);
	};

	auto 物体缩放变换 = [](float32 m){
		S_框架::g_变换手柄属性 = f_控制手柄_缩放物体(S_框架::g_操作变换轴, m);
	};

	auto 物体变换 = [&](const std::u16string& str){
		auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);
		
		float32 v = f_字符串转F32(str);
		switch (S_框架::g_物体变换方式) {
			case E_变换::e_移动: {
				物体移动变换(v);
				break;
			}
			case E_变换::e_旋转: {
				物体旋转变换(v, camera);
				break;
			}
			case E_变换::e_缩放: {
				物体缩放变换(v);
				break;
			}
		}
		f_camera_set偏移视点(*camera, f_global_get激活物体包围中心点());
	};

	auto 更新物体变换 = [&](){
		f_view3d_置变换手柄变换(S_框架::g_变换手柄属性);
		f_view3d_缩放所有视口变换手柄();
		f_工作区_联动更新(E_工作区类型::e_3D视口, E_工作区操作类型::e_变换物体);
	};

	if (S_框架::g_变换开关) {
		if (flag) {
			auto& str = f_prop_LineStr(view.m_数值输入显示);

			switch (f_input_getDXAsciiKey(c, E_平台键值类型::e_Win)) {
				case '\r': {
					S_框架::g_变换开关 = false;

					物体变换(str);
					//auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);
					//f_camera_set偏移视点(*camera, f_global_get激活物体包围中心点());
					//
					//float32 v = f_字符串转F32(str);
					//switch (S_框架::g_物体变换方式) {
					//	case E_变换::e_移动: {
					//		物体移动变换(v);
					//		break;
					//	}
					//	case E_变换::e_旋转: {
					//		物体旋转变换(v, camera);
					//		break;
					//	}
					//	case E_变换::e_缩放: {
					//		物体缩放变换(v);
					//		break;
					//	}
					//}

					
					f_控制手柄_完成变换(S_框架::g_物体变换方式);
					f_辅助物体_切换变换手柄轴显示(view.m_变换手柄, E_轴向::e_XYZ);
					f_3dView_开关物体数值输入(view, false);

					更新物体变换();
					//f_view3D_置变换手柄变换(S_框架::g_变换手柄属性);
					//f_view3d_缩放所有视口变换手柄();
					//f_工作区_联动更新(E_工作区类型::e_3D视口, E_工作区操作类型::e_变换物体);
					
					view.m_更新绘制属性 = true;
					view.f_关闭虚拟线绘制();
					break;
				}
				case '\b': {
					str.pop_back();
					view.ui_状态条->m_更新绘制属性 = true;
					物体变换(str);

					更新物体变换();
				}
				default:
					return false;
			}
			
		}
		else {
			if (f_input_数值输入(c)) {
				auto& str = f_prop_LineStr(view.m_数值输入显示);
				str += c;

				view.ui_状态条->m_更新绘制属性 = true;
				物体变换(str);

				//auto* camera = f_ui_get3D视口摄像机(view.ui_三维视口);
				//float32 v = f_字符串转F32(str);
				//switch (S_框架::g_物体变换方式) {
				//	case E_变换::e_移动: {
				//		物体移动变换(v);
				//		break;
				//	}
				//	case E_变换::e_旋转: {
				//		物体旋转变换(v, camera);
				//		break;
				//	}
				//	case E_变换::e_缩放: {
				//		物体缩放变换(v);
				//		break;
				//	}
				//}
				更新物体变换();
				//f_view3D_置变换手柄变换(S_框架::g_变换手柄属性);
				//f_view3d_缩放所有视口变换手柄();
				//f_工作区_联动更新(E_工作区类型::e_3D视口, E_工作区操作类型::e_变换物体);
			}
		}
		return true;
	}
	return false;
}

void f_3dView_开关物体数值输入(C_3D场景视口& box, bool 打开) {
	if (打开) {
		box.mf_按键字符输入 = f_3dView_物体数值输入;
		box.f_get窗口管理()->m_当前文本输入框 = &box;
	}
	else {
		box.mf_按键字符输入 = nullptr;
		box.f_get窗口管理()->m_当前文本输入框 = nullptr;

		auto& str = f_prop_LineStr(box.m_数值输入显示);
		str.clear();
		box.ui_状态条->m_更新绘制属性 = true;
	}
}

































E_事件是否传递 f_view3d_挤出(C_Widget* self, uint8 状态) {
	auto* camera = f_ui_get3D视口摄像机(self);

	switch (S_框架::g当前编辑对象类型) {
		case E_物体类型::t_网格物体:

			break;
		case E_物体类型::t_骨架: {
			std::vector<S_物体*> 物体组;

			for (auto& e : S_节点数据::g_编辑物体) {

				auto* 新节点 = f_节点构建_骨头节点(*self->m_UIctx, *S_节点框架::g_3D视口环境);

				auto ob = 新节点->m_NodeData->f_虚拟体();
				S_骨骼* 新骨骼 = f_ob_from骨骼(ob);
				新骨骼->m_长度 = 0;




				std::vector<C_节点基类*> 已有要链接的节点;
				if (e->m_Type == E_物体类型::t_骨骼) {
					已有要链接的节点 = f_NodeCtx_从可视物体取节点(std::vector{ e });

					新骨骼->m_骨节B->m_变换.position = f_ob_from骨骼(e)->m_骨节B->m_变换.position;
				}else if (e->m_Type == E_物体类型::t_实例) {
					已有要链接的节点 = f_NodeCtx_从可视物体取节点(std::vector{ e });
					//新骨骼->m_骨节B->m_变换.location = f_ob_from骨节(e)->m_骨节坐标;
					新骨骼->m_骨节B->m_变换.position = e->m_变换.position;
				}
				物体组.push_back(新骨骼->m_骨节B);
				if(已有要链接的节点.empty()) {
					std::cout<<"节点物体类型无实现"<<std::endl;
					continue;
				}


				std::vector<C_节点面板*> 上方节点面板组;
				for (auto& 当前节点 : 已有要链接的节点.front()->m_包含在多个视口的UI部件) {
					C_节点面板* 节点面板 = static_cast<C_节点面板*>(当前节点);
					if (节点面板) {
						vec2 p = 当前节点->f_getPos();
						p.y -= 当前节点->f_getSize().y * 2;

						//std::cout << "鼠标局部位置:" << p.x << " " << p.y << std::endl;
						f_NodeView_设置节点面板局部添加位置((C_节点视口*)(节点面板->m_节点当前所在视口), p, false);
					}
					上方节点面板组.push_back(节点面板);
				}

				f_NodeView_其他工作区添加面板到视口(新节点, true);
				f_NodePanel_链接节点(新节点, 1, static_cast<C_节点面板*>(上方节点面板组.front()), 1);

			}
			
			f_NodeCtx_clear选中编辑物体();
			for (auto& e : 物体组) {
				f_NodeCtx_set选中编辑物体(e, 1, true);
			}

			f_变换_准备移动物体(0);

			break;
		}
						 //case E_物体类型::t_骨骼:


		case E_物体类型::t_灯光:

			break;
		default:
			break;
	}
	return E_事件是否传递::e_事件传递_终止;
}


